ANÁLISIS ESTADÍSTICO PRODUCCIÓN MAÍZ

1 Resumen

En este informe se documenta el viaje de transformar un conjunto de datos brutos de las Evaluaciones Agropecuarias Municipales (EVA) en inteligencia de negocio para una empresa agrícola líder. El objetivo es caracterizar la producción de Maíz, estandarizando la información y corrigiendo inconsistencias para construir una base sólida que permita identificar los departamentos más productivos, detectar anomalías significativas y descubrir las tendencias que definirán las estrategias futuras de la compañía.

2 Introducción

Una importante empresa agrícola nacional, con un vasto portafolio de cultivos, se encuentra en una encrucijada estratégica. Para fortalecer su liderazgo en el mercado, necesita optimizar su operación de maíz, un pilar fundamental de su negocio. La dirección ha planteado preguntas clave: ¿En qué regiones debemos invertir? ¿Dónde se encuentran nuestras operaciones más eficientes? ¿Qué lecciones nos ha dejado la última década?

Para responder, se nos ha entregado una base de datos histórica, consolidada por un equipo anterior. Sin embargo, este activo digital presenta un desafío inicial: datos desorganizados, inconsistencias y vacíos de información. Nuestra misión como equipo de análisis de datos es embarcarnos en un viaje para convertir este caos en claridad, realizando un preprocesamiento exhaustivo para forjar una herramienta de análisis fiable y, finalmente, hacer que los datos cuenten la historia de la producción de maíz en Colombia.

3 Forjando la Herramienta

Todo gran análisis comienza con datos de alta calidad. Esta primera fase es la más crítica: es donde transformamos la materia prima —un conjunto de datos crudo y desordenado— en una base de datos limpia, coherente y lista para ser interrogada. Es el equivalente a afinar los instrumentos antes de un concierto.

3.1 Carga de Librerías y Segmentación del Cultivo

Nuestra historia comienza con un universo de información sobre toda la producción agrícola de la empresa. Para encontrar las respuestas sobre el maíz, nuestro primer paso es enfocar el lente, aislando la señal de nuestro cultivo de interés del ruido generado por los demás.

Explicación del código:Se cargan las librerías que nos acompañarán en este viaje analítico. Luego, se leen los datos y, mediante filter(), se segmenta el dataframe para crear una nueva base de datos que contiene exclusivamente los registros de MAÍZ

library(dplyr);library(stringr);library(ggplot2);library(tidyverse);library(knitr);library(kableExtra)
library(patchwork);library(tidyr);library(naniar);library(corrplot);library(htmltools)
library(crosstalk);library(mice);library(plotly);library(readxl);library(table1);library(highcharter)

datos <- readxl::read_excel("C:/Users/johnn/Desktop/GESTION DE DATOS/TALLER PREPROCESAMIENTO/DATOS/eva_df_2025.xlsx")
level_CULTIVO <- c(MAIZ = "MAIZ", maiz = "MAIZ")
datos <- datos %>% mutate(CULTIVO = recode(CULTIVO, !!!level_CULTIVO))
datos <- datos %>% filter(CULTIVO == "MAIZ")

3.2 Carga de Librerías y Segmentación del Cultivo

Los nombres de las columnas originales son poco intuitivos, lo que puede llevar a errores. Para trabajar de manera eficiente y clara, debemos establecer un lenguaje común.

Explicación del código: Se define un vector con nombres claros y estandarizados, y se asigna directamente a las columnas del dataframe datos. Ahora, todos en el equipo sabemos exactamente a qué se refiere cada variable.

nuevos_nombres <- c("departamento", "municipio", "grupo", "cultivo", "año",
                    "area_sembrada", "area_cosechada", "t_produccion",
                    "estado_fisico", "ciclo_cultivo")
names(datos) <- nuevos_nombres

3.3 Creación de la Variable Rendimiento

Producir mucho no es sinónimo de ser eficiente. La empresa necesita una métrica que mida la productividad por hectárea. Aquí es donde creamos al protagonista de nuestro análisis: la variable rendimiento.

Explicación del código: Usando ‘mutate()’, creamos la columna rendimiento. La fórmula t_produccion / area_cosechada se implementa con case_when() para manejar de forma robusta casos especiales, como divisiones por cero o datos faltantes, asegurando la calidad de nuestro indicador clave.

  datos <- datos %>%
  mutate(
    rendimiento = case_when(
      is.na(t_produccion) | is.na(area_cosechada) ~ NA_real_,
      area_cosechada == 0 ~ 0,
      TRUE ~ t_produccion / area_cosechada
    )
  )

3.4 Identificación de Datos Faltantes

Antes de poder limpiar o modelar, un analista debe actuar como un detective. El primer paso es realizar un diagnóstico completo para encontrar los “huecos” en la información. No podemos tratar un problema que no entendemos, por lo que este apartado se dedica a mapear dónde y cuántos datos faltantes existen en nuestro conjunto de datos.

Explicación del código: Se utilizan tres herramientas complementarias. Primero, summary() nos da un conteo rápido y numérico de los NAs en cada columna. Segundo, vis_miss() del paquete naniar genera una “radiografía” visual de la base de datos, permitiéndonos identificar patrones de datos faltantes de un solo vistazo. Finalmente, con filter(), extraemos y creamos una tabla específica con las filas exactas donde nuestra variable de interés, rendimiento, tiene información faltante, para una inspección detallada.

# 1. Resumen numérico de datos faltantes
table1(~ area_sembrada + area_cosechada + t_produccion + rendimiento | departamento, 
       data = datos)
Antioquia
(N=2096)
Cundinamarca
(N=1901)
Huila
(N=1715)
Santander
(N=1767)
Valle del Cauca
(N=1490)
Overall
(N=8969)
area_sembrada
Mean (SD) 217 (550) 166 (294) 218 (252) 97.8 (180) 191 (374) 179 (364)
Median [Min, Max] 40.0 [1.00, 6280] 60.0 [0, 3000] 140 [2.00, 1950] 50.0 [2.00, 3400] 55.0 [1.00, 3250] 60.0 [0, 6280]
area_cosechada
Mean (SD) 207 (536) 149 (277) 213 (246) 89.8 (170) 185 (366) 169 (354)
Median [Min, Max] 35.0 [0, 6200] 50.0 [0, 3000] 135 [2.00, 1650] 45.0 [0, 3350] 51.5 [0, 3250] 60.0 [0, 6200]
t_produccion
Mean (SD) 340 (1010) 237 (444) 543 (662) 179 (411) 1040 (2500) 442 (1230)
Median [Min, Max] 42.0 [0, 12100] 80.0 [0, 4990] 285 [2.00, 5250] 80.0 [0, 8270] 138 [0, 21100] 101 [0, 21100]
Missing 19 (0.9%) 23 (1.2%) 17 (1.0%) 17 (1.0%) 14 (0.9%) 90 (1.0%)
rendimiento
Mean (SD) 1.42 (0.708) 1.66 (0.763) 2.61 (1.21) 1.97 (1.00) 3.64 (2.20) 2.18 (1.45)
Median [Min, Max] 1.20 [0, 7.00] 1.50 [0, 8.75] 3.00 [0.545, 7.14] 1.81 [0, 14.0] 3.00 [0, 8.50] 1.60 [0, 14.0]
Missing 19 (0.9%) 23 (1.2%) 17 (1.0%) 17 (1.0%) 14 (0.9%) 90 (1.0%)
vis_miss(datos)

datos %>%
  filter(is.na(rendimiento)) %>%
  select(-rendimiento) %>% # Quitamos la columna 'rendimiento'
  head(10) %>% # Mostramos solo las primeras 10 filas para el informe
  kable(caption = "Muestra de 10 Registros con Rendimiento Faltante") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Muestra de 10 Registros con Rendimiento Faltante
departamento municipio grupo cultivo año area_sembrada area_cosechada t_produccion estado_fisico ciclo_cultivo
Santander SOCORRO CEREALES MAIZ 2006 77 65 NA GRANO SECO TRANSITORIO
Valle del Cauca CARTAGO CEREALES MAIZ 2006 1577 1577 NA GRANO SECO TRANSITORIO
Valle del Cauca TORO CEREALES MAIZ 2007 195 160 NA GRANO SECO TRANSITORIO
Huila TARQUI CEREALES MAIZ 2008 83 83 NA GRANO SECO TRANSITORIO
Valle del Cauca CARTAGO CEREALES MAIZ 2008 1342 1122 NA GRANO SECO TRANSITORIO
Huila PAICOL CEREALES MAIZ 2009 138 136 NA GRANO SECO TRANSITORIO
Valle del Cauca ZARZAL CEREALES MAIZ 2009 48 48 NA GRANO SECO TRANSITORIO
Antioquia LA ESTRELLA CEREALES MAIZ 2010 3 3 NA GRANO SECO TRANSITORIO
Huila OPORAPA CEREALES MAIZ 2011 150 145 NA GRANO SECO TRANSITORIO
Huila ACEVEDO CEREALES MAIZ 2012 825 823 NA GRANO SECO TRANSITORIO

3.5 Imputación de Datos Faltantes

Para asegurar la calidad del análisis, se evaluó el impacto de diferentes estrategias para el tratamiento de los datos faltantes. Se realizó una comparación visual (mediante diagramas de caja) de las distribuciones de rendimiento bajo tres escenarios: eliminando los registros faltantes, imputando con la media por departamento y usando una imputación múltiple con MICE.

# 1. Estandarizar texto a mayúsculas
datos <- datos %>% 
  mutate(departamento = str_to_upper(departamento))
#omitir datos
datos_limpios <- datos %>%
  filter(!is.na(datos$rendimiento))
#imputar por departamento
datos_imputados <- datos %>%
  group_by(departamento) %>%
  mutate(
    rendimiento = ifelse(is.na(rendimiento), 
                          mean(rendimiento, na.rm = TRUE), 
                          rendimiento)
  ) %>%
  ungroup()
# Imputación de datos faltantes con MICE
imputR <- mice(datos, m = 5, maxit = 5, seed = 123, print = FALSE) 
Datos_ImputR <- complete(imputR)

datos_todos <- bind_rows(
  datos_limpios %>% mutate(metodo_imputacion = "Sin NA"),
  datos_imputados %>% mutate(metodo_imputacion = "Imputacion Dept"),
  Datos_ImputR %>% mutate(metodo_imputacion = "Mice")
)
ggplot(datos_todos, aes(x = departamento, y = rendimiento, fill = metodo_imputacion)) +
  geom_boxplot(position = position_dodge(width = 0.8), outlier.alpha = 0.3) +
  labs(
    title = "Comparación de Métodos de Imputación de Rendimiento",
    x = "Departamento",
    y = "Rendimiento (unidades)"
  ) +
  scale_fill_manual(values = c("Sin NA" = "#781C2E", "Imputacion Dept" = "#E86C1F", "Mice" = "#FFB347")) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", color = "darkred", hjust = 0.5))

Análisis de los Resultados Se observó que las distribuciones de rendimiento para cada departamento eran prácticamente idénticas en los tres casos. Esta falta de variación se explica por la baja proporción de datos faltantes en el conjunto de datos (aproximadamente un 1%). Como regla general, cuando la cantidad de datos faltantes es inferior al 5%, el riesgo de introducir un sesgo significativo en los resultados es bajo, independientemente del método que se elija. A pesar de que el impacto fue insignificante, se optó por utilizar la imputación con MICE como método definitivo para conservar la totalidad de los registros, asegurando un conjunto de datos completo y robusto. El resultado es Datos_ImputR, nuestro set de datos final: completo, limpio y confiable para el análisis.

4 Extrayendo las Respuestas

Con nuestra herramienta de datos ya forjada y afinada, comienza la fase de exploración. Ahora podemos interrogar a los datos para que nos revelen sus secretos y nos guíen hacia decisiones de negocio más inteligentes.

4.1 Distribución de la muestra por departamentos

Antes de analizar el rendimiento, necesitamos mapear el terreno: ¿de dónde proviene nuestra información? Este mapa nos muestra la concentración de datos a nivel nacional.

#obtencion de frecuencia en la muestra
datos_freq <- Datos_ImputR %>%
  count(departamento)

#grafico de distribucion de muestra
p5 <- plot_ly(datos_freq, labels = ~departamento, values = ~n, type = "pie") %>%
  layout(title = list(text = "<b>DISTRIBUCIÓN DE LA MUESTRA POR DEPARTAMENTOS</b>",
      font = list(color = "darkred",  size = 15),x = 0.5, xanchor = "center"))
p5

Análisis de los Resultados La distribución de la muestra está bien repartida entre los departamentos, lo que es positivo para el análisis, ya que las conclusiones generales no estarán fuertemente sesgadas por la información de una sola región.

Llama la atención que Valle del Cauca, a pesar de ser el departamento con la menor cantidad de registros en este conjunto de datos, fue el que demostró tener el rendimiento más alto y consistente en los análisis anteriores. Esto sugiere que su alta productividad no es un artefacto de tener una muestra más grande, sino una característica real de la región.

4.2 Comparación del rendimiento en 2007 vs. 2017

El negocio no es estático. Para entender el presente, debemos mirar al pasado. Este análisis nos cuenta la historia de una década de cambio.

Explicación del código: Filtramos los datos para los años 2007 y 2017. geom_col con position = “dodge” crea un gráfico de barras comparativo que muestra la evolución del rendimiento mediano en cada departamento.

datos_comparacion <- Datos_ImputR %>% filter(año %in% c(2007, 2017)) %>% group_by(departamento, año) %>% summarise(rendimiento_mediano = median(rendimiento, na.rm = TRUE), .groups = 'drop')

p6 <- datos_comparacion %>%
  hchart("column", 
         hcaes(x = departamento, y = rendimiento_mediano, group = año)) %>%
  hc_title(text = "4.2. Comparación de Rendimiento Mediano: 2007 vs. 2017") %>%
  hc_yAxis(min = 0,
           max = 3.5,
           tickInterval = 0.5,
           title = list(text = "Rendimiento Mediano (Ton/Ha)")) %>%
  hc_xAxis(title = list(text = "Departamento")) %>%
  hc_colors(c("#E86C1F", "#781C2E")) %>%
  hc_legend(enabled = TRUE)
p6

Análisis de los Resultados [Espacio para su análisis e interpretación. ¿Qué departamentos mejoraron su rendimiento en esa década? ¿Alguno empeoró? ¿Qué cambios importantes se observan?]

4.3 Tendencia del rendimiento a través de los años

Esta visualización nos cuenta la historia completa a través del tiempo. ¿Qué departamentos han mostrado un crecimiento sostenido? ¿Cuáles se han estancado o han sido erráticos?

Explicación del código: Agrupamos por año y departamento para calcular el rendimiento mediano anual. geom_line conecta los puntos a lo largo del tiempo, revelando la trayectoria de cada departamento.

tendencia_anual <- Datos_ImputR %>%
  group_by(año, departamento) %>%
  summarise(rendimiento_mediano = median(rendimiento, na.rm = TRUE), .groups = 'drop')

p7 <- highchart() %>%
  hc_title(text = "4.3. Tendencia Anual del Rendimiento por Departamento") %>%
  hc_xAxis(title = list(text = "Año"),
           categories = sort(unique(tendencia_anual$año))) %>%
  hc_yAxis( max = 3.5,tickInterval = 0.5,title = list(text = "Rendimiento Mediano (Ton/Ha)")) %>%
  hc_add_series(data = tendencia_anual, 
                type = "line",
                hcaes(x = año, y = rendimiento_mediano, group = departamento)) %>%
  hc_colors(c("#781C2E", "#E86C1F", "#FFB347", "#5D001E", "#C1440E")) %>%
  hc_legend(enabled = TRUE, align = "center", verticalAlign = "bottom") %>%
  hc_chart(backgroundColor = "#FFFFFF") 
p7

Análisis de los Resultados [Espacio para su análisis e interpretación. ¿Qué departamentos mejoraron su rendimiento en esa década? ¿Alguno empeoró? ¿Qué cambios importantes se observan?]

4.4 Tablero Gráfico Resumen

Para la toma de decisiones estratégicas, los directivos necesitan una visión global y rápida. Este tablero de mando consolida nuestros hallazgos más importantes en una sola vista.

Explicación del código: La librería patchwork nos permite componer un tablero combinando nuestros gráficos (g1, g2, g4) con una sintaxis simple e intuitiva (+ y /), como si armáramos un rompecabezas.

browsable(
  tagList(
    # fila 1
    bscols(
      widths = c(6, 6),   # mitad y mitad
      p5,                 # plotly
      p6                  # highchart
    ),
    # fila 2
    div(style="margin-top:20px;", p7)  # ocupa toda la fila
  )
)

4.5 Tabla de Indicadores Descriptivos

Mientras que los gráficos nos dan la intuición, las decisiones de negocio requieren los números detrás de las imágenes. Esta tabla proporciona las estadísticas descriptivas precisas.

Explicación del código: table1() es una potente función que genera tablas con calidad de publicación. Con una simple fórmula (~ rendimiento | departamento), obtenemos un resumen estadístico completo, desglosado por departamento.

table1(~ rendimiento | departamento, data = Datos_ImputR, topclass="Rtable1-zebra")
ANTIOQUIA
(N=2096)
CUNDINAMARCA
(N=1901)
HUILA
(N=1715)
SANTANDER
(N=1767)
VALLE DEL CAUCA
(N=1490)
Overall
(N=8969)
rendimiento
Mean (SD) 1.43 (0.737) 1.68 (0.792) 2.61 (1.22) 1.97 (1.01) 3.63 (2.20) 2.18 (1.46)
Median [Min, Max] 1.20 [0, 7.40] 1.50 [0, 8.75] 3.00 [0.545, 7.14] 1.80 [0, 14.0] 3.00 [0, 8.50] 1.60 [0, 14.0]

Análisis de los Resultados [Espacio para su análisis e interpretación de la tabla. Puede comparar directamente la media y la desviación estándar entre departamentos, o comentar sobre los rangos (mínimo y máximo) para reforzar las conclusiones de los boxplots.]

Show All Code
Hide All Code